home *** CD-ROM | disk | FTP | other *** search
/ The Very Best of Atari Inside / The Very Best of Atari Inside 1.iso / mint / mint110s / dosdir.c < prev    next >
C/C++ Source or Header  |  1994-02-11  |  31KB  |  1,349 lines

  1. /*
  2. Copyright 1990,1991,1992 Eric R. Smith.
  3. Copyright 1992,1993,1994 Atari Corporation.
  4. All rights reserved.
  5. */
  6.  
  7. /* DOS directory functions */
  8.  
  9. #include "mint.h"
  10.  
  11. extern int aliasdrv[];    /* in filesys.c */
  12.  
  13. /* change to a new drive: should always return a map of valid drives */
  14.  
  15. long ARGS_ON_STACK
  16. d_setdrv(d)
  17.     int d;
  18. {
  19.     long r;
  20.     extern long dosdrvs;    /* in filesys.c */
  21.  
  22.     r = drvmap() | dosdrvs | PSEUDODRVS;
  23.  
  24.     TRACE(("Dsetdrv(%d)", d));
  25.     if (d < 0 || d >= NUM_DRIVES || (r & (1L << d)) == 0) {
  26.         DEBUG(("Dsetdrv: invalid drive %d", d));
  27.         return r;
  28.     }
  29.  
  30.     curproc->base->p_defdrv = curproc->curdrv = d;
  31.     return r;
  32. }
  33.  
  34.  
  35. long ARGS_ON_STACK
  36. d_getdrv()
  37. {
  38.     TRACE(("Dgetdrv"));
  39.     return curproc->curdrv;
  40. }
  41.  
  42. long ARGS_ON_STACK
  43. d_free(buf, d)
  44.     long *buf;
  45.     int d;
  46. {
  47.     fcookie *dir = 0;
  48.     FILESYS *fs;
  49.     fcookie root;
  50.     long r;
  51.  
  52.     TRACE(("Dfree(%d)", d));
  53.  
  54. /* drive 0 means current drive, otherwise it's d-1 */
  55.     if (d)
  56.         d = d-1;
  57.     else
  58.         d = curproc->curdrv;
  59.  
  60. /* If it's not a standard drive or an alias of one, get the pointer to
  61.    the filesystem structure and use the root directory of the
  62.    drive. */
  63.     if (d < 0 || d >= NUM_DRIVES) {
  64.         int i;
  65.  
  66.         for (i = 0; i < NUM_DRIVES; i++) {
  67.             if (aliasdrv[i] == d) {
  68.                 d = i;
  69.                 goto aliased;
  70.             }
  71.         }
  72.  
  73.         fs = get_filesys (d);
  74.         if (!fs)
  75.           return EDRIVE;
  76.         r = fs->root (d, &root);
  77.         if (r < 0)
  78.           return r;
  79.         r = (*fs->dfree) (&root, buf);
  80.         release_cookie (&root);
  81.         return r;
  82.     }
  83.  
  84. /* check for a media change -- we don't care much either way, but it
  85.  * does keep the results more accurate
  86.  */
  87.     (void)disk_changed(d);
  88.  
  89. aliased:
  90.  
  91. /* use current directory, not root, since it's more likely that
  92.  * programs are interested in the latter (this makes U: work much
  93.  * better)
  94.  */
  95.     dir = &curproc->curdir[d];
  96.     if (!dir->fs) {
  97.         DEBUG(("Dfree: bad drive"));
  98.         return EDRIVE;
  99.     }
  100.  
  101.     return (*dir->fs->dfree)(dir, buf);
  102. }
  103.  
  104. long ARGS_ON_STACK
  105. d_create(path)
  106.     const char *path;
  107. {
  108.     fcookie dir;
  109.     long r;
  110.     char temp1[PATH_MAX];
  111.  
  112.     TRACE(("Dcreate(%s)", path));
  113.  
  114.     r = path2cookie(path, temp1, &dir);
  115.     if (r) {
  116.         DEBUG(("Dcreate(%s): returning %ld", path, r));
  117.         return r;    /* an error occured */
  118.     }
  119. /* check for write permission on the directory */
  120.     r = dir_access(&dir, S_IWOTH);
  121.     if (r) {
  122.         DEBUG(("Dcreate(%s): write access to directory denied",path));
  123.         release_cookie(&dir);
  124.         return r;
  125.     }
  126.     r = (*dir.fs->mkdir)(&dir, temp1, DEFAULT_DIRMODE & ~curproc->umask);
  127.     release_cookie(&dir);
  128.     return r;
  129. }
  130.  
  131. long ARGS_ON_STACK
  132. d_delete(path)
  133.     const char *path;
  134. {
  135.     fcookie parentdir, targdir;
  136.     long r;
  137.     PROC *p;
  138.     int i;
  139.     XATTR xattr;
  140.     char temp1[PATH_MAX];
  141.  
  142.     TRACE(("Ddelete(%s)", path));
  143.  
  144.     r = path2cookie(path, temp1, &parentdir);
  145.  
  146.     if (r) {
  147.         DEBUG(("Ddelete(%s): error %lx", path, r));
  148.         release_cookie(&parentdir);
  149.         return r;
  150.     }
  151. /* check for write permission on the directory which the target
  152.  * is located
  153.  */
  154.     if ((r = dir_access(&parentdir, S_IWOTH)) != 0) {
  155.         DEBUG(("Ddelete(%s): access to directory denied", path));
  156.         release_cookie(&parentdir);
  157.         return r;
  158.     }
  159.  
  160. /* now get the info on the file itself */
  161.  
  162.     r = relpath2cookie(&parentdir, temp1, NULL, &targdir, 0);
  163.     if (r) {
  164. bailout:
  165.         release_cookie(&parentdir);
  166.         DEBUG(("Ddelete: error %ld on %s", r, path));
  167.         return r;
  168.     }
  169.     if ((r = (*targdir.fs->getxattr)(&targdir, &xattr)) != 0) {
  170.         release_cookie(&targdir);
  171.         goto bailout;
  172.     }
  173.  
  174. /* if the "directory" is a symbolic link, really unlink it */
  175.     if ( (xattr.mode & S_IFMT) == S_IFLNK ) {
  176.         r = (*parentdir.fs->remove)(&parentdir, temp1);
  177.     } else if ( (xattr.mode & S_IFMT) != S_IFDIR ) {
  178.         DEBUG(("Ddelete: %s is not a directory", path));
  179.         r = EPTHNF;
  180.     } else {
  181.  
  182. /* don't delete anyone else's root or current directory */
  183.         for (p = proclist; p; p = p->gl_next) {
  184.         if (p->wait_q == ZOMBIE_Q || p->wait_q == TSR_Q)
  185.             continue;
  186.         for (i = 0; i < NUM_DRIVES; i++) {
  187.             if (samefile(&targdir, &p->root[i])) {
  188.                 DEBUG(("Ddelete: directory %s is a root directory",
  189.                     path));
  190. noaccess:
  191.                 release_cookie(&targdir);
  192.                 release_cookie(&parentdir);
  193.                 return EACCDN;
  194.             } else if (samefile(&targdir, &p->curdir[i])) {
  195.                 if (i == p->curdrv && p != curproc) {
  196.                     DEBUG(("Ddelete: directory %s is in use",
  197.                         path));
  198.                     goto noaccess;
  199.                 } else {
  200.                     release_cookie(&p->curdir[i]);
  201.                     dup_cookie(&p->curdir[i], &p->root[i]);
  202.                 } 
  203.             }
  204.         }
  205.         }
  206.         release_cookie(&targdir);
  207.         r = (*parentdir.fs->rmdir)(&parentdir, temp1);
  208.     }
  209.     release_cookie(&parentdir);
  210.     return r;
  211. }
  212.  
  213. long ARGS_ON_STACK
  214. d_setpath(path)
  215.     const char *path;
  216. {
  217.     fcookie dir;
  218.     int drv = curproc->curdrv;
  219.     int i;
  220.     char c;
  221.     long r;
  222.     XATTR xattr;
  223.  
  224.     TRACE(("Dsetpath(%s)", path));
  225.  
  226.     r = path2cookie(path, follow_links, &dir);
  227.  
  228.     if (r) {
  229.         DEBUG(("Dsetpath(%s): returning %ld", path, r));
  230.         return r;
  231.     }
  232.  
  233.     if (path[0] && path[1] == ':') {
  234.         c = *path;
  235.         if (c >= 'a' && c <= 'z')
  236.             drv = c-'a';
  237.         else if (c >= 'A' && c <= 'Z')
  238.             drv = c-'A';
  239.     }
  240.  
  241.     r = (*dir.fs->getxattr)(&dir, &xattr);
  242.  
  243.     if (r < 0) {
  244.         DEBUG(("Dsetpath: file '%s': attributes not found", path));
  245.         release_cookie(&dir);
  246.         return r;
  247.     }
  248.  
  249.     if (!(xattr.attr & FA_DIR)) {
  250.         DEBUG(("Dsetpath(%s): not a directory",path));
  251.         release_cookie(&dir);
  252.         return EPTHNF;
  253.     }
  254.  
  255.     if (denyaccess(&xattr, S_IROTH|S_IXOTH)) {
  256.         DEBUG(("Dsetpath(%s): access denied", path));
  257.         release_cookie(&dir);
  258.         return EACCDN;
  259.     }
  260. /*
  261.  * watch out for symbolic links; if c:\foo is a link to d:\bar, then
  262.  * "cd c:\foo" should also change the drive to d:
  263.  */
  264.     if (drv != UNIDRV && dir.dev != curproc->root[drv].dev) {
  265.         for (i = 0; i < NUM_DRIVES; i++) {
  266.             if (curproc->root[i].dev == dir.dev &&
  267.                 curproc->root[i].fs == dir.fs) {
  268.                 if (drv == curproc->curdrv)
  269.                     curproc->curdrv = i;
  270.                 drv = i;
  271.                 break;
  272.             }
  273.         }
  274.     }
  275.     release_cookie(&curproc->curdir[drv]);
  276.     curproc->curdir[drv] = dir;
  277.     return 0;
  278. }
  279.  
  280. /* jr: like d_getpath, except that the caller provides a limit
  281.    for the max. number of characters to be put into the buffer.
  282.    Inspired by POSIX.1, getcwd(), 5.2.2 */
  283.  
  284. long ARGS_ON_STACK
  285. d_getcwd(path, drv, size)
  286.     char *path;
  287.     int drv, size;
  288. {
  289.     fcookie *dir, *root;
  290.     long r;
  291.     char buf[PATH_MAX];
  292.     FILESYS *fs;
  293.  
  294.     TRACE(("Dgetcwd(%c, %d)", drv + '@', size));
  295.     if (drv < 0 || drv > NUM_DRIVES)
  296.         return EDRIVE;
  297.  
  298.     drv = (drv == 0) ? curproc->curdrv : drv-1;
  299.  
  300.     root = &curproc->root[drv];
  301.  
  302.     if (!root->fs) {    /* maybe not initialized yet? */
  303.         changedrv(drv);
  304.         root = &curproc->curdir[drv];
  305.         if (!root->fs)
  306.             return EDRIVE;
  307.     }
  308.     fs = root->fs;
  309.     dir = &curproc->curdir[drv];
  310.  
  311.     if (!(fs->fsflags & FS_LONGPATH)) {
  312.         r = (*fs->getname)(root, dir, buf, PATH_MAX);
  313.         if (r) return r;
  314.         if (strlen(buf) < size) {
  315.             strcpy(path, buf);
  316.             return 0;
  317.         } else {
  318.             return ERANGE;
  319.         }
  320.     }
  321.     return (*fs->getname)(root, dir, path, size);
  322. }
  323.  
  324. long ARGS_ON_STACK
  325. d_getpath(path, drv)
  326.     char *path;
  327.     int drv;
  328. {
  329.     TRACE(("Dgetpath(%c)", drv + '@'));
  330.     return d_getcwd(path, drv, PATH_MAX);
  331. }
  332.  
  333. long ARGS_ON_STACK
  334. f_setdta(dta)
  335.     DTABUF *dta;
  336. {
  337.  
  338.     TRACE(("Fsetdta: %lx", dta));
  339.     curproc->dta = dta;
  340.     curproc->base->p_dta = (char *)dta;
  341.     return 0;
  342. }
  343.  
  344. long ARGS_ON_STACK
  345. f_getdta()
  346. {
  347.     long r;
  348.  
  349.     r = (long)curproc->dta;
  350.     TRACE(("Fgetdta: returning %lx", r));
  351.     return r;
  352. }
  353.  
  354. /*
  355.  * Fsfirst/next are actually implemented in terms of opendir/readdir/closedir.
  356.  */
  357.  
  358. long ARGS_ON_STACK
  359. f_sfirst(path, attrib)
  360.     const char *path;
  361.     int attrib;
  362. {
  363.     char *s, *slash;
  364.     FILESYS *fs;
  365.     fcookie dir, newdir;
  366.     DTABUF *dta;
  367.     DIR *dirh;
  368.     XATTR xattr;
  369.     long r;
  370.     int i, havelabel;
  371.     char temp1[PATH_MAX];
  372.  
  373.     TRACE(("Fsfirst(%s, %x)", path, attrib));
  374.  
  375.     r = path2cookie(path, temp1, &dir);
  376.  
  377.     if (r) {
  378.         DEBUG(("Fsfirst(%s): path2cookie returned %ld", path, r));
  379.         return r;
  380.     }
  381.  
  382. /*
  383.  * we need to split the last name (which may be a pattern) off from
  384.  * the rest of the path, even if FS_KNOPARSE is true
  385.  */
  386.     slash = 0;
  387.     s = temp1;
  388.     while (*s) {
  389.         if (*s == '\\')
  390.             slash = s;
  391.         s++;
  392.     }
  393.  
  394.     if (slash) {
  395.         *slash++ = 0;    /* slash now points to a name or pattern */
  396.         r = relpath2cookie(&dir, temp1, follow_links, &newdir, 0);
  397.         release_cookie(&dir);
  398.         if (r) {
  399.             DEBUG(("Fsfirst(%s): lookup returned %ld", path, r));
  400.             return r;
  401.         }
  402.         dir = newdir;
  403.     } else {
  404.         slash = temp1;
  405.     }
  406.  
  407. /* BUG? what if there really is an empty file name? */
  408.     if (!*slash) {
  409.         DEBUG(("Fsfirst: empty pattern"));
  410.         return EFILNF;
  411.     }
  412.  
  413.     fs = dir.fs;
  414.     dta = curproc->dta;
  415.  
  416. /* Now, see if we can find a DIR slot for the search. We use the following
  417.  * heuristics to try to avoid destroying a slot:
  418.  * (1) if the search doesn't use wildcards, don't bother with a slot
  419.  * (2) if an existing slot was for the same DTA address, re-use it
  420.  * (3) if there's a free slot, re-use it. Slots are freed when the
  421.  *     corresponding search is terminated.
  422.  */
  423.  
  424.     for (i = 0; i < NUM_SEARCH; i++) {
  425.         if (curproc->srchdta[i] == dta) {
  426.             dirh = &curproc->srchdir[i];
  427.             if (dirh->fc.fs) {
  428.                 (*dirh->fc.fs->closedir)(dirh);
  429.                 release_cookie(&dirh->fc);
  430.                 dirh->fc.fs = 0;
  431.             }
  432.             curproc->srchdta[i] = 0; /* slot is now free */
  433.         }
  434.     }
  435.  
  436. /* copy the pattern over into dta_pat into TOS 8.3 form */
  437. /* remember that "slash" now points at the pattern (it follows the last \,
  438.    if any)
  439.  */
  440.     copy8_3(dta->dta_pat, slash);
  441.  
  442. /* if attrib & FA_LABEL, read the volume label */
  443. /* BUG: the label date and time are wrong. Does it matter?
  444.  */
  445.     havelabel = 0;
  446.     if (attrib & FA_LABEL) {
  447.         r = (*fs->readlabel)(&dir, dta->dta_name, TOS_NAMELEN+1);
  448.         dta->dta_attrib = FA_LABEL;
  449.         dta->dta_time = dta->dta_date = 0;
  450.         dta->dta_size = 0;
  451.         dta->magic = EVALID;
  452.         if (r == 0 && !pat_match(dta->dta_name, dta->dta_pat))
  453.             r = EFILNF;
  454.         if (attrib == FA_LABEL)
  455.             return r;
  456.         else if (r == 0)
  457.             havelabel = 1;
  458.     }
  459.  
  460.     if (!havelabel && has_wild(slash) == 0) { /* no wild cards in pattern */
  461.         r = relpath2cookie(&dir, slash, follow_links, &newdir, 0);
  462.         if (r == 0) {
  463.             r = (*newdir.fs->getxattr)(&newdir, &xattr);
  464.             release_cookie(&newdir);
  465.         }
  466.         release_cookie(&dir);
  467.         if (r) {
  468.             DEBUG(("Fsfirst(%s): couldn't get file attributes",path));
  469.             return r;
  470.         }
  471.         dta->magic = EVALID;
  472.         dta->dta_attrib = xattr.attr;
  473.         dta->dta_time = xattr.mtime;
  474.         dta->dta_date = xattr.mdate;
  475.         dta->dta_size = xattr.size;
  476.         strncpy(dta->dta_name, slash, TOS_NAMELEN-1);
  477.         dta->dta_name[TOS_NAMELEN-1] = 0;
  478.         if (curproc->domain == DOM_TOS &&
  479.             !(fs->fsflags & FS_CASESENSITIVE))
  480.             strupr(dta->dta_name);
  481.         return 0;
  482.     }
  483.  
  484. /* There is a wild card. Try to find a slot for an opendir/readdir
  485.  * search. NOTE: we also come here if we were asked to search for
  486.  * volume labels and found one.
  487.  */
  488.     for (i = 0; i < NUM_SEARCH; i++) {
  489.         if (curproc->srchdta[i] == 0)
  490.             break;
  491.     }
  492.     if (i == NUM_SEARCH) {
  493.         int oldest = 0; long oldtime = curproc->srchtim[0];
  494.  
  495.         DEBUG(("Fsfirst(%s): having to re-use a directory slot!",path));
  496.         for (i = 1; i < NUM_SEARCH; i++) {
  497.             if (curproc->srchtim[i] < oldtime) {
  498.                 oldest = i;
  499.                 oldtime = curproc->srchtim[i];
  500.             }
  501.         }
  502.     /* OK, close this directory for re-use */
  503.         i = oldest;
  504.         dirh = &curproc->srchdir[i];
  505.         if (dirh->fc.fs) {
  506.             (*dirh->fc.fs->closedir)(dirh);
  507.             release_cookie(&dirh->fc);
  508.             dirh->fc.fs = 0;
  509.         }
  510.         curproc->srchdta[i] = 0;
  511.     }
  512.  
  513. /* check to see if we have read permission on the directory (and make
  514.  * sure that it really is a directory!)
  515.  */
  516.     r = dir_access(&dir, S_IROTH);
  517.     if (r) {
  518.         DEBUG(("Fsfirst(%s): access to directory denied (error code %ld)", path, r));
  519.         release_cookie(&dir);
  520.         return r;
  521.     }
  522.  
  523. /* set up the directory for a search */
  524.     dirh = &curproc->srchdir[i];
  525.     dirh->fc = dir;
  526.     dirh->index = 0;
  527.     dirh->flags = TOS_SEARCH;
  528.     r = (*dir.fs->opendir)(dirh, dirh->flags);
  529.     if (r != 0) {
  530.         DEBUG(("Fsfirst(%s): couldn't open directory (error %ld)",
  531.             path, r));
  532.         release_cookie(&dir);
  533.         return r;
  534.     }
  535.  
  536. /* mark the slot as in-use */
  537.     curproc->srchdta[i] = dta;
  538.  
  539. /* set up the DTA for Fsnext */
  540.     dta->index = i;
  541.     dta->magic = SVALID;
  542.     dta->dta_sattrib = attrib;
  543.  
  544. /* OK, now basically just do Fsnext, except that instead of ENMFIL we
  545.  * return EFILNF.
  546.  * NOTE: If we already have found a volume label from the search above,
  547.  * then we skip the f_snext and just return that.
  548.  */
  549.     if (havelabel)
  550.         return 0;
  551.  
  552.     r = f_snext();
  553.     if (r == ENMFIL) r = EFILNF;
  554.     if (r)
  555.         TRACE(("Fsfirst: returning %ld", r));
  556. /* release_cookie isn't necessary, since &dir is now stored in the
  557.  * DIRH structure and will be released when the search is completed
  558.  */
  559.     return r;
  560. }
  561.  
  562. /*
  563.  * Counter for Fsfirst/Fsnext, so that we know which search slots are
  564.  * least recently used. This is updated once per second by the code
  565.  * in timeout.c.
  566.  * BUG: 1/second is pretty low granularity
  567.  */
  568.  
  569. long searchtime;
  570.  
  571. long ARGS_ON_STACK
  572. f_snext()
  573. {
  574.     char buf[TOS_NAMELEN+1];
  575.     DTABUF *dta = curproc->dta;
  576.     FILESYS *fs;
  577.     fcookie fc;
  578.     int i;
  579.     DIR *dirh;
  580.     long r;
  581.     XATTR xattr;
  582.  
  583.     TRACE(("Fsnext"));
  584.  
  585.     if (dta->magic == EVALID) {
  586.         DEBUG(("Fsnext: DTA marked a failing search"));
  587.         return ENMFIL;
  588.     }
  589.     if (dta->magic != SVALID) {
  590.         DEBUG(("Fsnext: dta incorrectly set up"));
  591.         return EINVFN;
  592.     }
  593.  
  594.     i = dta->index;
  595.     dirh = &curproc->srchdir[i];
  596.     curproc->srchtim[i] = searchtime;
  597.  
  598.     fs = dirh->fc.fs;
  599.     if (!fs)        /* oops -- the directory got closed somehow */
  600.         return EINTRN;
  601.  
  602. /* BUG: f_snext and readdir should check for disk media changes */
  603.  
  604.     for(;;) {
  605.         r = (*fs->readdir)(dirh, buf, TOS_NAMELEN+1, &fc);
  606.  
  607.         if (r == ENAMETOOLONG) {
  608.             DEBUG(("Fsnext: name too long"));
  609.             continue;    /* TOS programs never see these names */
  610.         }
  611.         if (r != 0) {
  612. baderror:
  613.             if (dirh->fc.fs)
  614.                 (void)(*fs->closedir)(dirh);
  615.             release_cookie(&dirh->fc);
  616.             dirh->fc.fs = 0;
  617.             curproc->srchdta[i] = 0;
  618.             dta->magic = EVALID;
  619.             if (r != ENMFIL)
  620.                 DEBUG(("Fsnext: returning %ld", r));
  621.             return r;
  622.         }
  623.  
  624.         if (!pat_match(buf, dta->dta_pat))
  625.         {
  626.             release_cookie(&fc);
  627.             continue;    /* different patterns */
  628.         }
  629.  
  630.     /* check for search attributes */
  631.         r = (*fc.fs->getxattr)(&fc, &xattr);
  632.         if (r) {
  633.             DEBUG(("Fsnext: couldn't get file attributes"));
  634.             release_cookie(&fc);
  635.             goto baderror;
  636.         }
  637.     /* if the file is a symbolic link, try to find what it's linked to */
  638.         if ( (xattr.mode & S_IFMT) == S_IFLNK ) {
  639.             char linkedto[PATH_MAX];
  640.             r = (*fc.fs->readlink)(&fc, linkedto, PATH_MAX);
  641.             release_cookie(&fc);
  642.             if (r == 0) {
  643.             /* the "1" tells relpath2cookie that we read a link */
  644.                 r = relpath2cookie(&dirh->fc, linkedto,
  645.                     follow_links, &fc, 1);
  646.                 if (r == 0) {
  647.                 r = (*fc.fs->getxattr)(&fc, &xattr);
  648.                 release_cookie(&fc);
  649.                 }
  650.             }
  651.             if (r) {
  652.                 DEBUG(("Fsnext: couldn't follow link: error %ld",
  653.                     r));
  654.             }
  655.         } else {
  656.             release_cookie(&fc);
  657.         }
  658.  
  659.     /* silly TOS rules for matching attributes */
  660.         if (xattr.attr == 0) break;
  661.         if (xattr.attr & 0x21) break;
  662.         if (dta->dta_sattrib & xattr.attr)
  663.             break;
  664.     }
  665.  
  666. /* here, we have a match */
  667.     dta->dta_attrib = xattr.attr;
  668.     dta->dta_time = xattr.mtime;
  669.     dta->dta_date = xattr.mdate;
  670.     dta->dta_size = xattr.size;
  671.     strcpy(dta->dta_name, buf);
  672.  
  673.     if (curproc->domain == DOM_TOS && !(fs->fsflags & FS_CASESENSITIVE)) {
  674.         strupr(dta->dta_name);
  675.     }
  676.     return 0;
  677. }
  678.  
  679. long ARGS_ON_STACK
  680. f_attrib(name, rwflag, attr)
  681.     const char *name;
  682.     int rwflag;
  683.     int attr;
  684. {
  685.     fcookie fc;
  686.     XATTR xattr;
  687.     long r;
  688.  
  689.     TRACE(("Fattrib(%s, %d)", name, attr));
  690.  
  691.     r = path2cookie(name, follow_links, &fc);
  692.  
  693.     if (r) {
  694.         DEBUG(("Fattrib(%s): error %ld", name, r));
  695.         return r;
  696.     }
  697.  
  698.     r = (*fc.fs->getxattr)(&fc, &xattr);
  699.  
  700.     if (r) {
  701.         DEBUG(("Fattrib(%s): getxattr returned %ld", name, r));
  702.         release_cookie(&fc);
  703.         return r;
  704.     }
  705.  
  706.     if (rwflag) {
  707.         if (attr & (FA_LABEL|FA_DIR)) {
  708.             DEBUG(("Fattrib(%s): illegal attributes specified",name));
  709.             r = EACCDN;
  710.         } else if (curproc->euid && curproc->euid != xattr.uid) {
  711.             DEBUG(("Fattrib(%s): not the file's owner",name));
  712.             r = EACCDN;
  713.         } else if (xattr.attr & (FA_LABEL|FA_DIR)) {
  714.             DEBUG(("Fattrib(%s): file is a volume label "
  715.                   "or directory",name));
  716.             r = EACCDN;
  717.         } else {
  718.             r = (*fc.fs->chattr)(&fc, attr);
  719.         }
  720.         release_cookie(&fc);
  721.         return r;
  722.     } else {
  723.         release_cookie(&fc);
  724.         return xattr.attr;
  725.     }
  726. }
  727.  
  728. long ARGS_ON_STACK
  729. f_delete(name)
  730.     const char *name;
  731. {
  732.     fcookie dir, fc;
  733.     long r;
  734.     char temp1[PATH_MAX];
  735.     XATTR    xattr;
  736.  
  737.     TRACE(("Fdelete(%s)", name));
  738.  
  739. /* get a cookie for the directory the file is in */
  740.     if (( r = path2cookie(name, temp1, &dir) ))
  741.     {
  742.         DEBUG(("Fdelete: couldn't get directory cookie: error %ld", r));
  743.         return r;
  744.     }
  745.  
  746. /* check for write permission on directory */
  747.     r = dir_access(&dir, S_IWOTH);
  748.     if (r) {
  749.         DEBUG(("Fdelete(%s): write access to directory denied",name));
  750.         release_cookie(&dir);
  751.         return EACCDN;
  752.     }
  753.  
  754. /* now get the file attributes */
  755.     if ( (r = (*dir.fs->lookup)(&dir, temp1, &fc) ) != 0) {
  756.         DEBUG(("Fdelete: error %ld while looking for %s", r, temp1));
  757.         release_cookie(&dir);
  758.         return r;
  759.     }
  760.  
  761.     if (( r = (*fc.fs->getxattr)(&fc, &xattr) < 0 ))
  762.     {
  763.         release_cookie(&dir);
  764.         release_cookie(&fc);
  765.         DEBUG(("Fdelete: couldn't get file attributes: error %ld", r));
  766.         return r;
  767.     }
  768. /* see if we're allowed to kill it */
  769.     if (denyaccess(&xattr, S_IWOTH)) {
  770.         release_cookie(&dir);
  771.         release_cookie(&fc);
  772.         DEBUG(("Fdelete: file access denied"));
  773.         return EACCDN;
  774.     }
  775.  
  776.     r = (*dir.fs->remove)(&dir,temp1);
  777.  
  778.     release_cookie(&fc);
  779.     release_cookie(&dir);
  780.     return r;
  781. }
  782.  
  783. long ARGS_ON_STACK
  784. f_rename(junk, old, new)
  785.     int junk;        /* ignored, for TOS compatibility */
  786.     const char *old, *new;
  787. {
  788.     fcookie olddir, newdir, oldfil;
  789.     XATTR xattr;
  790.     char temp1[PATH_MAX], temp2[PATH_MAX];
  791.     long r;
  792.  
  793.     UNUSED(junk);
  794.  
  795.     TRACE(("Frename(%s, %s)", old, new));
  796.  
  797.     r = path2cookie(old, temp2, &olddir);
  798.     if (r) {
  799.         DEBUG(("Frename(%s,%s): error parsing old name",old,new));
  800.         return r;
  801.     }
  802. /* check for permissions on the old file
  803.  * GEMDOS doesn't allow rename if the file is FA_RDONLY
  804.  * we enforce this restriction only on regular files; processes,
  805.  * directories, and character special files can be renamed at will
  806.  */
  807.     r = relpath2cookie(&olddir, temp2, (char *)0, &oldfil, 0);
  808.     if (r) {
  809.         DEBUG(("Frename(%s,%s): old file not found",old,new));
  810.         release_cookie(&olddir);
  811.         return r;
  812.     }
  813.     r = (*oldfil.fs->getxattr)(&oldfil, &xattr);
  814.     release_cookie(&oldfil);
  815.     if (r ||
  816.         ((xattr.mode & S_IFMT) == S_IFREG && (xattr.attr & FA_RDONLY)) )
  817.     {
  818.         DEBUG(("Frename(%s,%s): access to old file not granted",old,new));
  819.         release_cookie(&olddir);
  820.         return EACCDN;
  821.     }
  822.     r = path2cookie(new, temp1, &newdir);
  823.     if (r) {
  824.         DEBUG(("Frename(%s,%s): error parsing new name",old,new));
  825.         release_cookie(&olddir);
  826.         return r;
  827.     }
  828.  
  829.     if (newdir.fs != olddir.fs) {
  830.         DEBUG(("Frename(%s,%s): different file systems",old,new));
  831.         release_cookie(&olddir);
  832.         release_cookie(&newdir);
  833.         return EXDEV;    /* cross device rename */
  834.     }
  835.  
  836. /* check for write permission on both directories */
  837.     r = dir_access(&olddir, S_IWOTH);
  838.     if (!r) r = dir_access(&newdir, S_IWOTH);
  839.     if (r) {
  840.         DEBUG(("Frename(%s,%s): access to a directory denied",old,new));
  841.     } else {
  842.         r = (*newdir.fs->rename)(&olddir, temp2, &newdir, temp1);
  843.     }
  844.     release_cookie(&olddir);
  845.     release_cookie(&newdir);
  846.     return r;
  847. }
  848.  
  849. /*
  850.  * GEMDOS extension: Dpathconf(name, which)
  851.  * returns information about filesystem-imposed limits; "name" is the name
  852.  * of a file or directory about which the limit information is requested;
  853.  * "which" is the limit requested, as follows:
  854.  *    -1    max. value of "which" allowed
  855.  *    0    internal limit on open files, if any
  856.  *    1    max. number of links to a file    {LINK_MAX}
  857.  *    2    max. path name length        {PATH_MAX}
  858.  *    3    max. file name length        {NAME_MAX}
  859.  *    4    no. of bytes in atomic write to FIFO {PIPE_BUF}
  860.  *    5    file name truncation rules
  861.  *    6    file name case translation rules
  862.  *
  863.  * unlimited values are returned as 0x7fffffffL
  864.  *
  865.  * see also Sysconf() in dos.c
  866.  */
  867.  
  868. long ARGS_ON_STACK
  869. d_pathconf(name, which)
  870.     const char *name;
  871.     int which;
  872. {
  873.     fcookie dir;
  874.     long r;
  875.  
  876.     r = path2cookie(name, (char *)0, &dir);
  877.     if (r) {
  878.         DEBUG(("Dpathconf(%s): bad path",name));
  879.         return r;
  880.     }
  881.     r = (*dir.fs->pathconf)(&dir, which);
  882.     if (which == DP_CASE && r == EINVFN) {
  883.     /* backward compatibility with old .XFS files */
  884.         r = (dir.fs->fsflags & FS_CASESENSITIVE) ? DP_CASESENS :
  885.                 DP_CASEINSENS;
  886.     }
  887.     release_cookie(&dir);
  888.     return r;
  889. }
  890.  
  891. /*
  892.  * GEMDOS extension: Opendir/Readdir/Rewinddir/Closedir offer a new,
  893.  * POSIX-like alternative to Fsfirst/Fsnext, and as a bonus allow for
  894.  * arbitrary length file names
  895.  */
  896.  
  897. long ARGS_ON_STACK
  898. d_opendir(name, flag)
  899.     const char *name;
  900.     int flag;
  901. {
  902.     DIR *dirh;
  903.     fcookie dir;
  904.     long r;
  905.  
  906.     r = path2cookie(name, follow_links, &dir);
  907.     if (r) {
  908.         DEBUG(("Dopendir(%s): error %ld", name, r));
  909.         return r;
  910.     }
  911.     r = dir_access(&dir, S_IROTH);
  912.     if (r) {
  913.         DEBUG(("Dopendir(%s): read permission denied", name));
  914.         release_cookie(&dir);
  915.         return r;
  916.     }
  917.  
  918.     dirh = (DIR *)kmalloc(SIZEOF(DIR));
  919.     if (!dirh) {
  920.         release_cookie(&dir);
  921.         return ENSMEM;
  922.     }
  923.  
  924.     dirh->fc = dir;
  925.     dirh->index = 0;
  926.     dirh->flags = flag;
  927.     r = (*dir.fs->opendir)(dirh, flag);
  928.     if (r) {
  929.         DEBUG(("d_opendir(%s): opendir returned %ld", name, r));
  930.         release_cookie(&dir);
  931.         kfree(dirh);
  932.         return r;
  933.     }
  934.  
  935. /* we keep a chain of open directories so that if a process
  936.  * terminates without closing them all, we can clean up
  937.  */
  938.     dirh->next = curproc->searches;
  939.     curproc->searches = dirh;
  940.  
  941.     return (long)dirh;
  942. }
  943.  
  944. long ARGS_ON_STACK
  945. d_readdir(len, handle, buf)
  946.     int len;
  947.     long handle;
  948.     char *buf;
  949. {
  950.     DIR *dirh = (DIR *)handle;
  951.     fcookie fc;
  952.     long r;
  953.  
  954.     if (!dirh->fc.fs)
  955.         return EIHNDL;
  956.     r = (*dirh->fc.fs->readdir)(dirh, buf, len, &fc);
  957.     if (r == 0)
  958.         release_cookie(&fc);
  959.     return r;
  960. }
  961.  
  962. long ARGS_ON_STACK
  963. d_rewind(handle)
  964.     long handle;
  965. {
  966.     DIR *dirh = (DIR *)handle;
  967.  
  968.     if (!dirh->fc.fs)
  969.         return EIHNDL;
  970.     return (*dirh->fc.fs->rewinddir)(dirh);
  971. }
  972.  
  973. /*
  974.  * NOTE: there is also code in terminate() in dosmem.c that
  975.  * does automatic closes of directory searches.
  976.  * If you change d_closedir(), you may also need to change
  977.  * terminate().
  978.  */
  979.  
  980. long ARGS_ON_STACK
  981. d_closedir(handle)
  982.     long handle;
  983. {
  984.     long r;
  985.     DIR *dirh = (DIR *)handle;
  986.     DIR **where;
  987.  
  988.     where = &curproc->searches;
  989.     while (*where && *where != dirh) {
  990.         where = &((*where)->next);
  991.     }
  992.     if (!*where) {
  993.         DEBUG(("Dclosedir: not an open directory"));
  994.         return EIHNDL;
  995.     }
  996.  
  997. /* unlink the directory from the chain */
  998.     *where = dirh->next;
  999.  
  1000.     if (dirh->fc.fs) {
  1001.         r = (*dirh->fc.fs->closedir)(dirh);
  1002.         release_cookie(&dirh->fc);
  1003.     } else {
  1004.         r = 0;
  1005.     }
  1006.  
  1007.     if (r) {
  1008.         DEBUG(("Dclosedir: error %ld", r));
  1009.     }
  1010.     kfree(dirh);
  1011.     return r;
  1012. }
  1013.  
  1014. /*
  1015.  * GEMDOS extension: Fxattr gets extended attributes for a file. "flag"
  1016.  * is 0 if symbolic links are to be followed (like stat), 1 if not (like
  1017.  * lstat).
  1018.  */
  1019.  
  1020. long ARGS_ON_STACK
  1021. f_xattr(flag, name, xattr)
  1022.     int flag;
  1023.     const char *name;
  1024.     XATTR *xattr;
  1025. {
  1026.     fcookie fc;
  1027.     long r;
  1028.  
  1029.     TRACE(("Fxattr(%d, %s)", flag, name));
  1030.  
  1031.     r = path2cookie(name, flag ? (char *)0 : follow_links, &fc);
  1032.     if (r) {
  1033.         DEBUG(("Fxattr(%s): path2cookie returned %ld", name, r));
  1034.         return r;
  1035.     }
  1036.     r = (*fc.fs->getxattr)(&fc, xattr);
  1037.     if (r) {
  1038.         DEBUG(("Fxattr(%s): returning %ld", name, r));
  1039.     }
  1040.     release_cookie(&fc);
  1041.     return r;
  1042. }
  1043.  
  1044. /*
  1045.  * GEMDOS extension: Flink(old, new) creates a hard link named "new"
  1046.  * to the file "old".
  1047.  */
  1048.  
  1049. long ARGS_ON_STACK
  1050. f_link(old, new)
  1051.     const char *old, *new;
  1052. {
  1053.     fcookie olddir, newdir;
  1054.     char temp1[PATH_MAX], temp2[PATH_MAX];
  1055.     long r;
  1056.  
  1057.     TRACE(("Flink(%s, %s)", old, new));
  1058.  
  1059.     r = path2cookie(old, temp2, &olddir);
  1060.     if (r) {
  1061.         DEBUG(("Flink(%s,%s): error parsing old name",old,new));
  1062.         return r;
  1063.     }
  1064.     r = path2cookie(new, temp1, &newdir);
  1065.     if (r) {
  1066.         DEBUG(("Flink(%s,%s): error parsing new name",old,new));
  1067.         release_cookie(&olddir);
  1068.         return r;
  1069.     }
  1070.  
  1071.     if (newdir.fs != olddir.fs) {
  1072.         DEBUG(("Flink(%s,%s): different file systems",old,new));
  1073.         release_cookie(&olddir);
  1074.         release_cookie(&newdir);
  1075.         return EXDEV;    /* cross device link */
  1076.     }
  1077.  
  1078. /* check for write permission on the destination directory */
  1079.  
  1080.     r = dir_access(&newdir, S_IWOTH);
  1081.     if (r) {
  1082.         DEBUG(("Flink(%s,%s): access to directory denied",old,new));
  1083.     } else
  1084.         r = (*newdir.fs->hardlink)(&olddir, temp2, &newdir, temp1);
  1085.     release_cookie(&olddir);
  1086.     release_cookie(&newdir);
  1087.     return r;
  1088. }
  1089.  
  1090. /*
  1091.  * GEMDOS extension: Fsymlink(old, new): create a symbolic link named
  1092.  * "new" that contains the path "old".
  1093.  */
  1094.  
  1095. long ARGS_ON_STACK
  1096. f_symlink(old, new)
  1097.     const char *old, *new;
  1098. {
  1099.     fcookie newdir;
  1100.     long r;
  1101.     char temp1[PATH_MAX];
  1102.  
  1103.     TRACE(("Fsymlink(%s, %s)", old, new));
  1104.  
  1105.     r = path2cookie(new, temp1, &newdir);
  1106.     if (r) {
  1107.         DEBUG(("Fsymlink(%s,%s): error parsing %s", old,new,new));
  1108.         return r;
  1109.     }
  1110.     r = dir_access(&newdir, S_IWOTH);
  1111.     if (r) {
  1112.         DEBUG(("Fsymlink(%s,%s): access to directory denied",old,new));
  1113.     } else
  1114.         r = (*newdir.fs->symlink)(&newdir, temp1, old);
  1115.     release_cookie(&newdir);
  1116.     return r;
  1117. }
  1118.  
  1119. /*
  1120.  * GEMDOS extension: Freadlink(buflen, buf, linkfile):
  1121.  * read the contents of the symbolic link "linkfile" into the buffer
  1122.  * "buf", which has length "buflen".
  1123.  */
  1124.  
  1125. long ARGS_ON_STACK
  1126. f_readlink(buflen, buf, linkfile)
  1127.     int buflen;
  1128.     char *buf;
  1129.     const char *linkfile;
  1130. {
  1131.     fcookie file;
  1132.     long r;
  1133.     XATTR xattr;
  1134.  
  1135.     TRACE(("Freadlink(%s)", linkfile));
  1136.  
  1137.     r = path2cookie(linkfile, (char *)0, &file);
  1138.     if (r) {
  1139.         DEBUG(("Freadlink: unable to find %s", linkfile));
  1140.         return r;
  1141.     }
  1142.     r = (*file.fs->getxattr)(&file, &xattr);
  1143.     if (r) {
  1144.         DEBUG(("Freadlink: unable to get attributes for %s", linkfile));
  1145.     } else if ( (xattr.mode & S_IFMT) == S_IFLNK )
  1146.         r = (*file.fs->readlink)(&file, buf, buflen);
  1147.     else {
  1148.         DEBUG(("Freadlink: %s is not a link", linkfile));
  1149.         r = EACCDN;
  1150.     }
  1151.     release_cookie(&file);
  1152.     return r;
  1153. }
  1154.  
  1155. /*
  1156.  * GEMDOS extension: Dcntl(): do file system specific functions
  1157.  */
  1158.  
  1159. long ARGS_ON_STACK
  1160. d_cntl(cmd, name, arg)
  1161.     int cmd;
  1162.     const char *name;
  1163.     long arg;
  1164. {
  1165.     fcookie dir;
  1166.     long r;
  1167.     char temp1[PATH_MAX];
  1168.  
  1169.     TRACE(("Dcntl(cmd=%x, file=%s, arg=%lx)", cmd, name, arg));
  1170.  
  1171.     r = path2cookie(name, temp1, &dir);
  1172.     if (r) {
  1173.         DEBUG(("Dcntl: couldn't find %s", name));
  1174.         return r;
  1175.     }
  1176.     r = (*dir.fs->fscntl)(&dir, temp1, cmd, arg);
  1177.     release_cookie(&dir);
  1178.     return r;
  1179. }
  1180.  
  1181. /*
  1182.  * GEMDOS extension: Fchown(name, uid, gid) changes the user and group
  1183.  * ownerships of a file to "uid" and "gid" respectively.
  1184.  */
  1185.  
  1186. long ARGS_ON_STACK
  1187. f_chown(name, uid, gid)
  1188.     const char *name;
  1189.     int uid, gid;
  1190. {
  1191.     fcookie fc;
  1192.     XATTR xattr;
  1193.     long r;
  1194.  
  1195.     TRACE(("Fchown(%s, %d, %d)", name, uid, gid));
  1196.  
  1197.     r = path2cookie(name, follow_links, &fc);
  1198.     if (r) {
  1199.         DEBUG(("Fchown(%s): error %ld", name, r));
  1200.         return r;
  1201.     }
  1202.  
  1203. /* MiNT acts like _POSIX_CHOWN_RESTRICTED: a non-privileged process can
  1204.  * only change the ownership of a file that is owned by this user, to
  1205.  * the effective group id of the process
  1206.  */
  1207.     if (curproc->euid) {
  1208.         if (curproc->egid != gid)
  1209.             r = EACCDN;
  1210.         else
  1211.             r = (*fc.fs->getxattr)(&fc, &xattr);
  1212.         if (r) {
  1213.             DEBUG(("Fchown(%s): unable to get file attributes",name));
  1214.             release_cookie(&fc);
  1215.             return r;
  1216.         }
  1217.         if (xattr.uid != curproc->euid || xattr.uid != uid) {
  1218.             DEBUG(("Fchown(%s): not the file's owner",name));
  1219.             release_cookie(&fc);
  1220.             return EACCDN;
  1221.         }
  1222.     }
  1223.     r = (*fc.fs->chown)(&fc, uid, gid);
  1224.     release_cookie(&fc);
  1225.     return r;
  1226. }
  1227.  
  1228. /*
  1229.  * GEMDOS extension: Fchmod(file, mode) changes a file's access
  1230.  * permissions.
  1231.  */
  1232.  
  1233. long ARGS_ON_STACK
  1234. f_chmod(name, mode)
  1235.     const char *name;
  1236.     unsigned mode;
  1237. {
  1238.     fcookie fc;
  1239.     long r;
  1240.     XATTR xattr;
  1241.  
  1242.     TRACE(("Fchmod(%s, %o)", name, mode));
  1243.     r = path2cookie(name, follow_links, &fc);
  1244.     if (r) {
  1245.         DEBUG(("Fchmod(%s): error %ld", name, r));
  1246.         return r;
  1247.     }
  1248.     r = (*fc.fs->getxattr)(&fc, &xattr);
  1249.     if (r) {
  1250.         DEBUG(("Fchmod(%s): couldn't get file attributes",name));
  1251.     }
  1252.     else if (curproc->euid && curproc->euid != xattr.uid) {
  1253.         DEBUG(("Fchmod(%s): not the file's owner",name));
  1254.         r = EACCDN;
  1255.     } else {
  1256.         r = (*fc.fs->chmode)(&fc, mode & ~S_IFMT);
  1257.         if (r) DEBUG(("Fchmod: error %ld", r));
  1258.     }
  1259.     release_cookie(&fc);
  1260.     return r;
  1261. }
  1262.  
  1263. /*
  1264.  * GEMDOS extension: Dlock(mode, dev): locks or unlocks access to
  1265.  * a BIOS device. "mode" bit 0 is 0 for unlock, 1 for lock; "dev" is a
  1266.  * BIOS device (0 for A:, 1 for B:, etc.).
  1267.  *
  1268.  * Returns: 0 if the operation was successful
  1269.  *          EACCDN if a lock attempt is made on a drive that is being
  1270.  *            used
  1271.  *        ELOCKED if the drive is locked by another process
  1272.  *        ENSLOCK if a program attempts to unlock a drive it
  1273.  *            hasn't locked.
  1274.  * ++jr: if mode bit 1 is set, then instead of returning ELOCKED the
  1275.  * pid of the process which has locked the drive is returned (unless
  1276.  * it was locked by pid 0, in which case ELOCKED is still returned).
  1277.  */
  1278.  
  1279. PROC *dlockproc[NUM_DRIVES];
  1280.  
  1281. long ARGS_ON_STACK
  1282. d_lock(mode, dev)
  1283.     int mode, dev;
  1284. {
  1285.     PROC *p;
  1286.     FILEPTR *f;
  1287.     int i;
  1288.  
  1289.     TRACE(("Dlock(%x,%c:)", mode, dev+'A'));
  1290.     if (dev < 0 || dev >= NUM_DRIVES) return EDRIVE;
  1291.     if (aliasdrv[dev]) {
  1292.         dev = aliasdrv[dev] - 1;
  1293.         if (dev < 0 || dev >= NUM_DRIVES)
  1294.             return EDRIVE;
  1295.     }
  1296.     if ( (mode&1) == 0) {    /* unlock */
  1297.         if (dlockproc[dev] == curproc) {
  1298.             dlockproc[dev] = 0;
  1299.             changedrv(dev);
  1300.             return 0;
  1301.         }
  1302.         DEBUG(("Dlock: no such lock"));
  1303.         return ENSLOCK;
  1304.     }
  1305.  
  1306. /* code for locking */
  1307. /* is the drive already locked? */
  1308.     if (dlockproc[dev]) {
  1309.         DEBUG(("Dlock: drive already locked"));
  1310. #if 0
  1311.         if (dlockproc[dev] == curproc) return 0;
  1312. #endif
  1313.         if (dlockproc[dev]->pid == 0) return ELOCKED;
  1314.         return (mode & 2) ? dlockproc[dev]->pid : ELOCKED;
  1315.     }
  1316. /* see if the drive is in use */
  1317.     for (p = proclist; p; p = p->gl_next) {
  1318.         if (p->wait_q == ZOMBIE_Q || p->wait_q == TSR_Q)
  1319.             continue;
  1320.         for (i = MIN_HANDLE; i < MAX_OPEN; i++) {
  1321.             if ( ((f = p->handle[i]) != 0) && (f->fc.dev == dev) ) {
  1322.         DEBUG(("Dlock: process %d has an open handle on the drive", p->pid));
  1323.                 if (p->pid == 0) return EACCDN;
  1324.                 return (mode & 2) ? p->pid : EACCDN;
  1325.             }
  1326.         }
  1327.     }
  1328.  
  1329. /* if we reach here, the drive is not in use */
  1330. /* we lock it by setting dlockproc and by setting all root and current
  1331.  * directories referring to the device to a null file system
  1332.  */
  1333.     for (p = proclist; p; p = p->gl_next) {
  1334.         for (i = 0; i < NUM_DRIVES; i++) {
  1335.             if (p->root[i].dev == dev) {
  1336.                 release_cookie(&p->root[i]);
  1337.                 p->root[i].fs = 0;
  1338.             }
  1339.             if (p->curdir[i].dev == dev) {
  1340.                 release_cookie(&p->curdir[i]);
  1341.                 p->curdir[i].fs = 0;
  1342.             }
  1343.         }
  1344.     }
  1345.  
  1346.     dlockproc[dev] = curproc;
  1347.     return 0;
  1348. }
  1349.